#!/usr/bin/env python
# -*- coding: utf-8 -*-

'''Module for parsing GCC mapfiles'''
import re


'''Regexes'''
section_regex = re.compile(r"^(\S+)\s+(0x[0-9a-fA-F]+)\s+(0x[0-9a-fA-F]+)\s*load address")
load_section_regex = re.compile(r"^(\S+)\s+(0x[0-9a-fA-F]+)\s+(0x[0-9a-fA-F]+)\s*$")
multiline_section_a_regex = re.compile(r"^(\S+)\s*$")
multiline_section_b_regex = re.compile(r"\s*(0x[0-9a-fA-F]+)\s+(0x[0-9a-fA-F]+)\s*\Z")

input_section_regex = re.compile(r"^\s+(\S+)\s+(0x[0-9a-fA-F]+)\s+(0x[0-9a-fA-F]+)\s+(\S+)\s*$")
multiline_a_input_section_regex = re.compile(r"^\s+(\S+)\s*$")
multiline_b_input_section_regex = re.compile(r"^\s+(0x[0-9a-fA-F]+)\s+(0x[0-9a-fA-F]+)\s+(\S+)\s*$")

symbol_regex = re.compile(r"^\s+(0x[0-9a-fA-F]+)\s+(\S+)")

current_output_section = None
current_input_section = None
output_file = None

def parse_gcc(mapfile, outputfile):
    global current_output_section
    global current_input_section
    global output_file
    output_file = outputfile
    '''Function accepting the filename of a DIAB mapfile and returning a list of tuples'''
    with iter(open(mapfile)) as gcc_map:
        xml_startdoc()
        try:
            line = gcc_map.next()
            while(line is not None):
                line = line[:-1]
                '''Regular section'''
                section_match = section_regex.findall(line)
                if section_match:
                    section_name = section_match[0][0]
                    section_addr = section_match[0][1]
                    section_size = section_match[0][2]
                    xml_newoutputsection(section_name, section_addr, section_size)
                    current_input_section = None
                    current_output_section = section_name
                    line = gcc_map.next()
                    continue

                '''Section with load address'''
                load_section_match = load_section_regex.findall(line)
                if load_section_match:
                    section_name = load_section_match[0][0]
                    section_addr = load_section_match[0][1]
                    section_size = load_section_match[0][2]
                    xml_newoutputsection(section_name, section_addr, section_size)
                    current_input_section = None
                    current_output_section = section_name
                    line = gcc_map.next()
                    continue

                '''Multiline section'''
                multiline_section_a_match = multiline_section_a_regex.findall(line)
                if(multiline_section_a_match) :
                    line = gcc_map.next()
                    multiline_section_b_match = multiline_section_b_regex.findall(line)
                    if(multiline_section_b_match) :
                        section_name = multiline_section_a_match[0]
                        section_addr = multiline_section_b_match[0][0]
                        section_size = multiline_section_b_match[0][1]
                        xml_newoutputsection(section_name, section_addr, section_size)
                        current_input_section = None
                        current_output_section = section_name
                        line = gcc_map.next()
                        continue

                if(current_output_section is not None) :
                    '''Regular input section'''
                    input_section_match = input_section_regex.findall(line)
                    if input_section_match:
                        input_section_name = input_section_match[0][0]
                        input_section_addr = input_section_match[0][1]
                        input_section_size = input_section_match[0][2]
                        input_section_origin = input_section_match[0][3]
                        xml_newinputsection(input_section_name, input_section_addr, input_section_size, input_section_origin)
                        current_input_section = input_section_name
                        line = gcc_map.next()
                        continue

                    '''Multiline input section'''
                    multiline_a_input_section_match = multiline_a_input_section_regex.findall(line)
                    if(multiline_a_input_section_match):
                        line = gcc_map.next()
                        multiline_b_input_section_match = multiline_b_input_section_regex.findall(line)
                        if(multiline_b_input_section_match) :
                            input_section_name = multiline_a_input_section_match[0]
                            input_section_addr = multiline_b_input_section_match[0][0]
                            input_section_size = multiline_b_input_section_match[0][1]
                            input_section_origin = multiline_b_input_section_match[0][2]
                            xml_newinputsection(input_section_name, input_section_addr, input_section_size, input_section_origin)
                            current_input_section = input_section_name
                            line = gcc_map.next()
                            continue
                        else :
                            continue

                    '''Symbols'''
                    if current_input_section is not None:
                        symbol_match = symbol_regex.findall(line)
                        if(symbol_match):
                            symbol_addr = symbol_match[0][0]
                            symbol_name = symbol_match[0][1]
                            xml_newsymbol(symbol_name, symbol_addr)
                            line = gcc_map.next()
                            continue

                line = gcc_map.next()
        except StopIteration:
            xml_closedoc()


def xml_startdoc():
    '''Begin XML document'''
    with open(output_file, 'a') as out:
        out.write("<output_sections>\n")

def xml_closedoc():
    '''Add closing tags to XML document'''
    with open(output_file, 'a') as out:
        out.write("                </symbols>\n")
        out.write("            </input_section>\n")
        out.write("        </input_sections>\n")
        out.write("    </output_section>\n")
        out.write("</output_sections>")

def xml_newoutputsection(name, addr, size):
    '''Add new output section to XML'''
    with open(output_file, 'a') as out:
        if current_input_section is not None:
            out.write("                </symbols>\n")
            out.write("            </input_section>\n")
        if current_output_section is not None:
            out.write("        </input_sections>\n")
            out.write("    </output_section>\n")
        out.write("    <output_section>\n")
        out.write("        <name>{name}</name>\n".format(name=name))
        out.write("        <address>{addr}</address>\n".format(addr=addr))
        out.write("        <size>{size}</size>\n".format(size=size))
        out.write("        <input_sections>\n")

def xml_newinputsection(name, addr, size, origin):
    '''Add new input section to XML'''
    with open(output_file, 'a') as out:
        if current_input_section is not None:
            out.write("                </symbols>\n")
            out.write("            </input_section>\n")
        out.write("            <input_section>\n")
        out.write("                <name>{name}</name>\n".format(name=name))
        out.write("                <address>{addr}</address>\n".format(addr=addr))
        out.write("                <size>{size}</size>\n".format(size=size))
        out.write("                <origin>{origin}</origin>\n".format(origin=origin))
        out.write("                <symbols>\n")

def xml_newsymbol(name, addr):
    '''Add new symbol to XML'''
    with open(output_file, 'a') as out:
        out.write("                    <symbol>\n")
        out.write("                        <name>{name}</name>\n".format(name=name))
        out.write("                        <address>{addr}</address>\n".format(addr=addr))
        out.write("                    </symbol>\n")

def gcc_extract_labels(mapfile):
    '''Function accepting the filename of a DIAB mapfile and returning a list of tuples'''
    matchlist = []
    with open(mapfile) as gcc_map:
        for line in gcc_map:
            regex = re.compile(r"^\s*(0x[0-9a-fA-F]+)\s*([a-zA-Z0-9_]+)\s*=")
            match = regex.findall(line)
            if(match):
                matchtuple = (match[0][1], int(match[0][0], 16))
                matchlist.append(matchtuple)
    return matchlist
